/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * @brief map-messenger /map-messenger/src/main.cpp
 * Copyright (C) Matheus Neder, Amanda Duarte, Priscila Cardoso 2010
 * 
 * main.cpp is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * main.cpp is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * @brief map-messenger /map-messenger/src/main.cpp
 * @file main.cpp
 * @date 04/06/2010
 * @author Matheus Neder <matheusneder@gmail.com>
 * @author Amanda Duarte <mandinhaduarte89@gmail.com>
 * @author Priscila Cardoso <japinha-htinha@hotmail.com>
 */

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <string>
//#include <boost/bind.hpp>
//#include <boost/smart_ptr.hpp>
//#include <boost/asio.hpp>
//#include <boost/thread.hpp>
#include "MapSocket.h"
#include <exception>
#include "MapInteger.h"
#include "ModUtil.h"
#include <libglademm/xml.h>
#include <gtkmm.h>
#include "GuiInterface.h"
#include "Crypto.h"

using namespace std;

long iterationsIsFermatPrime = 0;
long iterationsGenerateFermatPrime = 0;
long iterationModPow = 0;

GuiInterface* gui = NULL;
MapInteger key = 1455555444;
Glib::ustring usernameConnectedWith = "Matheus";
Crypto* cpr = NULL;

const MapInteger MIN_PRIME = 2;
const MapInteger MAX_GEN = 1000;

int main(int argc, char *argv[])
{
	srand(time(NULL));
	//	cout << generatePrime(20000) << endl;
	//	return 0;
	cout << "* MAP Messenger *" << endl << endl;
	string username;
	static char buffer[255];
	cout << "Digite o seu nome: ";
	cin.getline(buffer, 254);
	while (strlen(buffer) < 2)
	{
		cout << "* O nome digitado nao eh valido." << endl;
		cout << "Por gentiliza, digite o seu nome: ";
		cin.getline(buffer, 254);
	}
	username = buffer;
	cout << endl;
	cout << "Ola \"" << username << "\". Escolha uma das opcoes disponiveis:"
			<< endl;
	cout << endl;

	int op;

	for (;;)
	{
		cout << " 1 - Aguardar uma conexao." << endl;
		cout << " 2 - Me conectar a um usuario remoto." << endl;
		cout << endl;
		cout << "Opcao: ";
		cin.getline(buffer, 254);
		if (buffer[0] == '1')
		{
			op = 1;
			break;
		}
		else if (buffer[0] == '2')
		{
			op = 2;
			break;
		}
		else
		{
			cout << endl;
			cout << "* Opcao invalida!" << endl;
			cout << endl;
		}
	}
	int lPort = 0;
	string host;
	switch (op)
	{
	case 1:
		do
		{
			cout << endl;
			cout
					<< "Digite a porta tcp/ip na qual deseja ouvir (1000 < porta < 10000): ";
			cin.getline(buffer, 254);
			lPort = atoi(buffer);
			if (lPort <= 1000 || lPort >= 10000)
			{
				cout << endl;
				cout << "* Valor invalido ou nao permitido!";
				cout << endl;
			}
		} while (lPort < 1000 || lPort >= 10000);
		cout << endl;
		break;
	case 2:
		cout << endl;
		cout
				<< "Digite o nome (ou end. ip) da maquina a qual deseja se conectar: ";
		cin.getline(buffer, 254);
		while (strlen(buffer) < 2)
		{
			cout << endl;
			cout << "* Endereco invalido!" << endl;
			cout << endl;
			cout
					<< "Digite o nome (ou end. ip) da maquina a qual deseja se conectar: ";
			cin.getline(buffer, 254);
			cout << endl;
		}
		host = buffer;
		do
		{
			cout << endl;
			cout
					<< "Digite a porta a qual se conectar em '" << host << "': ";
			cin.getline(buffer, 254);
			lPort = atoi(buffer);
			if (lPort <= 0 || lPort >= 100000)
			{
				cout << endl;
				cout << "* Valor invalido ou nao permitido!";
				cout << endl;
			}
		} while (lPort < 0 || lPort >= 100000);
		cout << endl;
		break;
	}
	boost::thread* thReader = NULL;
	MapSocket conn;
	MapInteger p, g, a, yc, ya;
	try
	{
		//		if (argc > 1)
		//		{
		//if (argv[1][0] == 'c')
		if (op == 2)
		{
			conn.connect(host, lPort);
			if (conn.getIsConnected())
			{
				p = generatePrime(MIN_PRIME);
				g = MapIntegerUtil::rand(1, MAX_GEN);
				a = MapIntegerUtil::rand(1, p - 1);
				yc = modPow(g, a, p);
				MapSocket::plainWrite(conn.getSocketPtr(), p);
				MapSocket::plainWrite(conn.getSocketPtr(), g);
				MapSocket::plainWrite(conn.getSocketPtr(), yc);
				ya = MapSocket::plainRead(conn.getSocketPtr());

				key = modPow(ya, a, p);
				cpr = new Crypto(key);

				cout << "p   (gerado e enviado)    = " << p << endl;
				cout << "g   (gerado e enviado)    = " << g << endl;
				cout << "a   (gerado)              = " << a
						<< " (minha chave secreta)" << endl;
				cout << "Ya  (calculado e enviado) = " << yc << endl;
				cout << "Yb  (recebido)            = " << ya << endl;
				cout << "key (calculado)           = " << key << endl;
				cout << "Tamanho da chave:           " << (sizeof(MapInteger)
						* 8) << " bits" << endl;
				cout << "Tamanho do bloco:           " << sizeof(MapInteger)
						<< " bytes" << endl;

				MapSocket::writeUsername(conn.getSocketPtr(), username);
				MapSocket::readUsernameConnectedWith(conn.getSocketPtr());

				cout << endl;
				cout << "* VERIFIQUE SE A JANELA GRAFICA FOI ABERTA *" << endl;
				cout << endl;
			}
		}
	//	else if (argv[1][0] == 'a')
		else if (op == 1)
		{
			cout << "Aguardando uma conexao..." << endl;
			cout << endl;
			conn.accept(lPort);
			if (conn.getIsConnected())
			{
				p = MapSocket::plainRead(conn.getSocketPtr());
				g = MapSocket::plainRead(conn.getSocketPtr());
				a = MapIntegerUtil::rand(1, p - 1);
				yc = MapSocket::plainRead(conn.getSocketPtr());
				ya = modPow(g, a, p);
				MapSocket::plainWrite(conn.getSocketPtr(), ya);
				key = modPow(yc, a, p);
				cpr = new Crypto(key);

				cout << "p   (recebido)            = " << p << endl;
				cout << "g   (recebido)            = " << g << endl;
				cout << "b   (gerado)              = " << a
						<< " (minha chave secreta)" << endl;
				cout << "Ya  (recebido)            = " << yc << endl;
				cout << "Yb  (calculado e enviado) = " << ya << endl;
				cout << "key (calculado)           = " << key << endl;
				cout << "Tamanho da chave:           " << (sizeof(MapInteger)
						* 8) << " bits" << endl;
				cout << "Tamanho do bloco:           " << sizeof(MapInteger)
						<< " bytes" << endl;

				MapSocket::readUsernameConnectedWith(conn.getSocketPtr());
				MapSocket::writeUsername(conn.getSocketPtr(), username);
				//
				cout << endl;
				cout << "* VERIFIQUE SE A JANELA GRAFICA FOI ABERTA *" << endl;
				cout << endl;

			}
			//			}
			//			else
			//			{
			//				cout << "argumento invalido" << endl;
			//				exit(1);
			//			}

		}
		else
		{
			cout << "argumento invalido" << endl;
			exit(1);
		}
	} catch (boost::system::system_error& e)
	{
		//TODO: exibir mensagem em caixa de dialogo e etc...
		cerr << e.what() << endl;
		cout << endl;
		cout << "Pressione <ENTER> para sair.";
		cin.getline(buffer, 254);
		exit(1);
	}
	if (conn.getIsConnected())
	{
		thReader = new boost::thread(boost::bind(MapSocket::read,
				conn.getSocketSharedPtr()));
	}
	else
	{
		//TODO: ver mesangem
		cout << "Nao foi possivel se conectar!" << endl;
		cout << endl;
		cout << "Pressione <ENTER> para sair.";
		cin.getline(buffer, 254);
		exit(1);
	}

	Gtk::Main kit(argc, argv);
	try
	{
		gui = new GuiInterface(conn);
		Gtk::Window* mainWindow = gui->getMainWindow();
		//		mainWindow->set_sensitive(false);
		//		Gtk::Assistant* connectionAssistent = gui->getConnectionAssitant();
		//		connectionAssistent->show();
		//mainWindow->set_position(Gtk::WIN_POS_CENTER_ON_PARENT);
		mainWindow->set_urgency_hint();
		kit.run(*mainWindow);
	} catch (const Gnome::Glade::XmlError& e)
	{
		Gtk::MessageDialog::MessageDialog* errorDialog = gui->fatalErrorDialog(
				Glib::ustring("Não foi possível carregar o MAP Messenger: ")
						+ e.what());
		kit.run(*errorDialog);
	}
	if (gui != NULL)
	{
		delete gui;
	}

	if (thReader != NULL)
	{
		thReader->join();
		delete thReader;
	}

	//	if (sock != NULL)
	//	{
	//		sock->close();
	//		delete sock;
	//	}

	return 0;
}
